<script setup>
import {ref, onMounted, onBeforeUnmount, defineProps, computed, watch} from 'vue'
import { ElLoading } from 'element-plus'
import * as THREE from 'three'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'

const props = defineProps({
  modelUrl: {
    type: String,
    required: true
  },
  mtlUrl: {
    type: String,
    required: false
  }
});


// DOM 元素引用
const container = ref(null)

// Three.js 对象
let scene, camera, renderer, controls, loadModelObject

// 模型加载进度
const modelLoadProgress = ref("未开始");

// 模型缩放
const modelScale = ref(1);

// 🔦 灯光控制参数（响应式）
const ambientIntensity = ref(3)     // 环境光
const directionalIntensity = ref(3) // 主光源
const backLightIntensity = ref(3)   // 背光（新增）

// 初始化场景
function initScene(){
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xf0f0f0)

  // 透视相机
  const width = container.value.clientWidth
  const height = container.value.clientHeight
  camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)
  camera.position.set(0, 0, 5)

  // 渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(width, height)
  renderer.setPixelRatio(window.devicePixelRatio)
  container.value.appendChild(renderer.domElement)

  // 添加轨道控制器（鼠标旋转缩放）
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.25
  // 禁用相机缩放（这样滚轮不会移动相机）
  controls.enableZoom = false;
  // 添加滚轮事件
  container.value.addEventListener('wheel', (event) => {
    event.preventDefault();
    // 使用 event.deltaY 判断滚轮方向
    const scaleFactor = event.deltaY > 0 ? 0.95 : 1.05; // 每次缩放 5%
    if (loadModelObject) {
      loadModelObject.scale.multiplyScalar(scaleFactor);
    }
    // 同步到 modelScale（用于 UI 显示）
    modelScale.value = loadModelObject.scale.x;
  }, { passive: false });

  // 环境光：全局照明
  const ambientLight = new THREE.AmbientLight(0xffffff, ambientIntensity.value)
  ambientLight.name = 'ambientLight'
  scene.add(ambientLight)

  // 主光源：从前方斜上方照亮
  const directionalLight = new THREE.DirectionalLight(0xffffff, directionalIntensity.value)
  directionalLight.position.set(1, 1, 1)
  directionalLight.name = 'directionalLight'
  scene.add(directionalLight)

  // 🔦 新增：背光（防止背面过暗）
  const backLight = new THREE.DirectionalLight(0xffffff, backLightIntensity.value)
  backLight.position.set(-1, 0, -1) // 从背面打光
  backLight.name = 'backLight'
  scene.add(backLight)

  // 监听窗口大小变化
  window.addEventListener('resize', onWindowResize)
}

// 更新灯光函数
function updateLights() {
  const ambientLight = scene.getObjectByName('ambientLight')
  const directionalLight = scene.getObjectByName('directionalLight')
  const backLight = scene.getObjectByName('backLight')

  if (ambientLight) ambientLight.intensity = ambientIntensity.value
  if (directionalLight) directionalLight.intensity = directionalIntensity.value
  if (backLight) backLight.intensity = backLightIntensity.value
}

// 更新灯光（绑定到响应式变量）
function updateAmbient()   { updateLights() }
function updateDirectional(){ updateLights() }
function updateBackLight() { updateLights() }

// 加载 OBJ 模型
function loadObjModel(objLoader, objUrl){
  objLoader.load(
      objUrl,
      (object) => {
        console.log('OBJ 模型加载开始...')
        object.position.set(0, 0, 0)
        // 使用响应式 scale
        object.scale.set(modelScale.value, modelScale.value, modelScale.value);
        // 保存模型
        loadModelObject = object;
        scene.add(object)
        console.log('OBJ 模型加载完成:', object)
        modelLoadProgress.value = "模型加载完成"

        setTimeout(()=>{
          modelLoadProgress.value = "ok";
        }, 1000);

        // 关闭 loading
        if (loadingInstance.value) {
          loadingInstance.value.close()
          loadingInstance.value = null
        }
      },
      (xhr) => {
        // console.log("OBJ 模型加载进度",xhr.loaded , xhr.total)
        modelLoadProgress.value = `(${xhr.loaded} / ${xhr.total})`
      },
      (error) => {
        console.error('OBJ 加载失败:', error)
        modelLoadProgress.value = "模型加载失败"
        // 关闭 loading
        if (loadingInstance.value) {
          loadingInstance.value.close()
          loadingInstance.value = null
        }
      }
  )
}

// 加载三维模型
// 加载三维模型（支持 .obj/.mtl 或 .glb）
function loadModel(objUrl, mtlUrl) {
  console.log('加载模型:', objUrl, mtlUrl)
  // 显示 loading，文本为“开始加载...”
  loadingInstance.value = ElLoading.service({
    target: container.value,
    lock: true,
    text: '开始加载模型...',
    spinner: 'el-icon-loading',
    background: 'rgba(0, 0, 0, 0.5)'
  })


  const ext = objUrl.toLowerCase().split('.').pop()

  if (ext === 'glb' || ext === 'gltf') {
    const gltfLoader = new GLTFLoader()
    modelLoadProgress.value = "开始加载 GLB 模型..."

    gltfLoader.load(
        objUrl,
        (gltf) => {
          const object = gltf.scene
          object.position.set(0, 0, 0)
          object.scale.set(modelScale.value, modelScale.value, modelScale.value)
          loadModelObject = object
          scene.add(object)
          console.log('GLB 模型加载完成:', object)
          modelLoadProgress.value = "GLB 模型加载完成"

          setTimeout(()=>{
            modelLoadProgress.value = "ok"
          },300);
          // 关闭 loading
          if (loadingInstance.value) {
            loadingInstance.value.close()
            loadingInstance.value = null
          }
        },
        (xhr) => {
          const progress = Math.round((xhr.loaded / xhr.total) * 100)
          modelLoadProgress.value = `GLB: ${progress}%`
        },
        (error) => {
          console.error('GLB 加载失败:', error)
          modelLoadProgress.value = "GLB 加载失败"
          // 关闭 loading
          if (loadingInstance.value) {
            loadingInstance.value.close()
            loadingInstance.value = null
          }
        }
    )
  } else {
    // 原来的 OBJ + MTL 加载逻辑
    const mtlLoader = new MTLLoader()
    modelLoadProgress.value = "开始加载 MTL 材质..."

    mtlLoader.load(
        mtlUrl,
        (materials) => {
          const objLoader = new OBJLoader()
          materials.preload()
          objLoader.setMaterials(materials)
          loadObjModel(objLoader, objUrl)
        },
        (xhr) => {
          const progress = Math.round((xhr.loaded / xhr.total) * 100)
          modelLoadProgress.value = `MTL: ${progress}%`
        },
        (error) => {
          console.error('MTL 加载失败:', error)
          modelLoadProgress.value = "MTL 加载失败"
          // 关闭 loading
          if (loadingInstance.value) {
            loadingInstance.value.close()
            loadingInstance.value = null
          }
        }
    )
  }
}

// 窗口大小调整处理
function onWindowResize(){
  const width = container.value.clientWidth
  const height = container.value.clientHeight-300;
  camera.aspect = width / height
  camera.updateProjectionMatrix()
  renderer.setSize(width, height)
}

// 动画循环控制变量
let animationId = null
// 动画循环
const animate = () => {
  if (!renderer || !scene || !camera) return
  // 只有 controls 存在才更新
  if (controls) {
    controls.update()
  }
  // 👉 关键：检查模型的实际 scale 是否变化，并同步到 modelScale
  if (loadModelObject && Math.abs(loadModelObject.scale.x - modelScale.value) > 0.001) {
    // 避免频繁更新，加个阈值
    modelScale.value = loadModelObject.scale.x; // 假设 xyz 缩放一致
  }

  renderer.render(scene, camera)
  // 继续下一帧
  animationId = requestAnimationFrame(animate)
}

// 更新模型缩放
function updateModelScale() {
  if (loadModelObject) {
    loadModelObject.scale.set(
        modelScale.value,
        modelScale.value,
        modelScale.value
    );
  }
}

// 📥 下载当前模型
async function downloadCurrentModel() {
  const objUrl = props.modelUrl
  const mtlUrl = props.mtlUrl
  const ext = objUrl.toLowerCase().split('.').pop()

  try {
    if (ext === 'glb') {
      // ✅ 情况 1: GLB 直接下载
      const response = await fetch(objUrl)
      const blob = await response.blob()
      saveAs(blob, getFileName(objUrl))
    } else if (ext === 'obj' && mtlUrl) {
      // ✅ 情况 2: OBJ + MTL 打包成 ZIP
      const zip = new JSZip()

      // 下载 OBJ 文件并加入 ZIP
      let objBlob = await (await fetch(objUrl)).blob()
      zip.file(getFileName(objUrl), objBlob)

      // 下载 MTL 文件并加入 ZIP
      let mtlBlob = await (await fetch(mtlUrl)).blob()
      zip.file(getFileName(mtlUrl), mtlBlob)

      // 处理 MTL 中引用的纹理贴图（如果有 map_Kd 等）
      const mtlText = await mtlBlob.text()
      const textureMatches = [...mtlText.matchAll(/map_K\w+\s+([^\s]+)/g)]
      const texturePromises = textureMatches.map(async (match) => {
        const texPath = match[1].trim()
        // 假设纹理和 MTL 在同一目录（可调整）
        const texUrl = new URL(texPath, mtlUrl).href
        try {
          const texRes = await fetch(texUrl)
          if (texRes.ok) {
            const texBlob = await texRes.blob()
            const texName = getFileName(texPath)
            zip.file(texName, texBlob)
            console.log('已添加纹理:', texName)
          }
        } catch (err) {
          console.warn(`纹理加载失败: ${texPath}`, err)
        }
      })

      await Promise.all(texturePromises)

      // 生成并下载 ZIP
      const zipBlob = await zip.generateAsync({ type: 'blob' })
      saveAs(zipBlob, 'model_package.zip')
    } else {
      ElMessage.warning('不支持的模型格式或缺少 MTL 文件')
    }
  } catch (error) {
    console.error('下载失败:', error)
    ElMessage.error('下载失败，请检查网络或文件路径')
  }
}

// 辅助函数：从 URL 提取文件名
function getFileName(url) {
  return decodeURIComponent(url.split('/').pop().split('?')[0]) || 'model'
}

// loading 实例引用
const loadingInstance = ref(null);

watch(modelLoadProgress,
    (newVal) => {
      if (loadingInstance.value && newVal) {
        loadingInstance.value.setText("加载中："+newVal)
      }
    },
    { immediate: true } // 立即执行一次，以防初始值就有内容
)

// 组件挂载后初始化
onMounted(() => {
  // 初始化场景
  initScene()
  // 加载模型
  loadModel(props.modelUrl, props.mtlUrl);
  // 启动动画循环
  animate()
})

// 组件卸载前清理
onBeforeUnmount(() => {
  window.removeEventListener('resize', onWindowResize)

  // 停止动画循环
  if (animationId !== null) {
    cancelAnimationFrame(animationId)
    animationId = null
  }

  // 清理渲染器、场景等
  if (renderer && renderer.domElement && container.value) {
    container.value.removeChild(renderer.domElement)
  }
  if (renderer) renderer.dispose()

  if (scene) {
    scene.traverse((child) => {
      if (child.geometry) child.geometry.dispose()
      if (child.material) child.material.dispose()
    })
  }

  // 确保 loading 被关闭
  if (loadingInstance.value) {
    loadingInstance.value.close()
    loadingInstance.value = null
  }

  // 释放引用
  scene = null
  camera = null
  renderer = null
  controls = null // 关键：置空前动画必须已停止
})
</script>

<template>

  <div ref="container" style="width: 100%; height: 100vh;"></div>

  <!-- 缩放控制 UI -->
  <div class="scaleControl">
    <label>模型缩放：</label>
    <input
        type="range"
        min="0.01"
        max="5"
        step="0.01"
        v-model="modelScale"
        @input="updateModelScale"
    />
    <span>{{ Number(modelScale).toFixed(2) }}</span>
  </div>

<!--  模型加载进度-->
  <div class="loadProgress" v-if="modelLoadProgress!=='ok'">
    加载进度：{{modelLoadProgress}}
  </div>

  <!-- 🔦 灯光控制 UI -->
  <div class="lightControl">
    <h4>灯光控制</h4>
    <div>
      <label>环境光: {{ Number(ambientIntensity).toFixed(2) }}</label>
      <input type="range" min="0" max="20" step="0.01" v-model="ambientIntensity" @input="updateAmbient" />
    </div>
    <div>
      <label>主光源: {{ Number(directionalIntensity).toFixed(2) }}</label>
      <input type="range" min="0" max="20" step="0.01" v-model="directionalIntensity" @input="updateDirectional" />
    </div>
    <div>
      <label>背光: {{ Number(backLightIntensity).toFixed(2) }}</label>
      <input type="range" min="0" max="20" step="0.01" v-model="backLightIntensity" @input="updateBackLight" />
    </div>
  </div>

  <div class="downloadControl" v-if="modelLoadProgress==='ok'" @click="downloadCurrentModel">
    <el-button type="primary" size="small" >下载当前模型</el-button>
  </div>
</template>

<style scoped lang="scss">
/* 确保容器有尺寸 */
div {
  overflow: hidden;
}

.downloadControl{
  position: fixed;      /* 固定定位：相对于视口 */
  bottom: 32vh;         /* 距离底部 20px */
  left: 20px;           /* 距离左侧 20px */
  padding: 10px 16px;
  color: white;         /* 文字颜色 */
  font-size: 14px;
  border-radius: 4px;
  z-index: 999;         /* 确保在最上层 */
  //pointer-events: none; /* 防止遮挡点击（可选） */
  cursor: pointer;
}

/* 模型加载进度悬浮在左下角 */
.loadProgress {
  min-width: 12vw;
  height: 2vh;
  position: fixed;      /* 固定定位：相对于视口 */
  bottom: 32vh;         /* 距离底部 20px */
  left: 20px;           /* 距离左侧 20px */
  padding: 10px 16px;
  background-color: rgba(158, 158, 158, 0.7); /* 半透明背景 */
  color: white;         /* 文字颜色 */
  font-size: 14px;
  border-radius: 4px;
  z-index: 999;         /* 确保在最上层 */
  pointer-events: none; /* 防止遮挡点击（可选） */
}

.scaleControl {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background-color: rgba(158, 158, 158, 0.7);
  color: white;
  padding: 10px;
  border-radius: 4px;
  z-index: 999;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.scaleControl input[type="range"] {
  width: 120px;
}

// 灯光控制
.lightControl {
  width: 12vw;
  height: 25vh;
  position: fixed;
  bottom: 1vh;
  left: 20px;
  background: rgba(158, 158, 158, 0.7);
  color: white;
  padding: 12px;
  border-radius: 6px;
  z-index: 999;
  font-size: 12px;
}

.lightControl h4 {
  margin: 0 0 8px 0;
  font-size: 14px;
}

.lightControl div {
  margin: 6px 0;
}

.lightControl label {
  display: block;
  margin-bottom: 4px;
}

.lightControl input[type="range"] {
  width: 100%;
}

</style>
